package utils.sl651;

import lombok.Data;

/**
 * SL651协议报文校验分析器
 * 
 * 根据报文的编码类型调用相应的分析和解析方法
 */
public class SL651MessageValidator {
    
    /**
     * 检测SL651协议报文类型
     * 根据SL651-2014标准：ASCII编码以01开头，HEX编码以7E7E开头
     * 
     * @param message 原始消息
     * @return 报文格式类型（ASCII/HEX/UNKNOWN/null）
     */
    public static String detectProtocolType(String message) {
        // 清理消息（去除空格和换行符）
        String cleanMessage = message.replaceAll("\\s+", "").toUpperCase();
        
        // 1. 检查ASCII编码格式（以01开头 - SOH起始符）
        if (cleanMessage.startsWith("01")) {
            return "ASCII";
        }
        
        // 2. 检查HEX编码格式（以7E7E开头）
        if (cleanMessage.startsWith("7E7E")) {
            return "HEX";
        }
        
        // 3. 兼容性检查：其他可能的SL651格式
        if (isValidSL651Format(cleanMessage)) {
            return "UNKNOWN";
        }
        
        return null; // 不是SL651报文
    }

    /**
     * 验证是否为有效的SL651格式（兼容性检查）
     */
    public static boolean isValidSL651Format(String message) {
        // 检查是否为纯十六进制字符串
        if (!message.matches("^[0-9A-F]+$")) {
            return false;
        }

        // 检查长度（最小90字符，符合SL651最小报文要求）
        if (message.length() < 90) {
            return false;
        }

        // 检查是否包含常见的SL651功能码（在第17-18位置）
        if (message.length() >= 18) {
            String possibleFuncCode = message.substring(16, 18);
            // 常见功能码: 2F,30,31,32,33,34,35,36,37,38
            if (possibleFuncCode.matches("^(2F|3[0-8])$")) {
                return true;
            }
        }

        return false;
    }
    
    /**
     * 检查是否为完整的SL651报文
     * 
     * @param message 报文内容
     * @return 是否完整
     */
    public static boolean isCompleteSL651Message(String message) {
        String clean = message.replaceAll("\\s+", "").toUpperCase();
        
        // 检查起始符
        boolean isAsciiFormat = clean.startsWith("01");
        boolean isHexFormat = clean.startsWith("7E7E");
        
        if (!isAsciiFormat && !isHexFormat) {
            return false;
        }
        
        // 根据格式检查最小长度
        int minLength = isHexFormat ? 60 : 90; // HEX格式报文通常更短
        if (clean.length() < minLength) {
            return false;
        }
        
        // 检查结束符和CRC
        // SL651报文以ETX(03)结尾，然后是CRC校验码
        int etxPos = clean.lastIndexOf("03");
        if (etxPos == -1) {
            return false;
        }
        
        // 检查ETX后是否有足够的CRC校验码（至少4字符）
        int crcLength = clean.length() - etxPos - 2;
        if (crcLength < 4) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 获取报文信息（完整解析，报文解析入口，流程是先校验再解析）
     * 
     * @param message 报文内容
     * @return 基本信息字符串
     */
    public static SL651Message getMessageInfo(String message) throws Exception {
            if (message == null || message.length() < 24) {
                throw new  Exception("报文长度不足");
            }

            SL651Message sl651Message = null;
            String protocolType = detectProtocolType(message);
            String clean = message;

            if (!"ASCII".equals(protocolType)) {
                clean = clean.replaceAll("\\s+", "").toUpperCase();
            }

            ValidationResult result = validateMessageFormatAndCrc(message);
            System.out.println(result.getValidateInfo()); // 打印验证信息

            // 根据报文类型提取基本信息
            String type = detectProtocolType(message);

            if ("ASCII".equals(type)) {
                // ASCII编码报文
                sl651Message  = SL651AscillDecoder.decode( clean);
            } else if ("HEX".equals(type)) {
                // HEX编码报文
                sl651Message  = SL651HexDecoder.decode( clean);
            } else {
                throw new  Exception( "未知报文类型");
            }
            return sl651Message;
    }
    
    /**
     * 校验报文基本格式是否正确并验证CRC
     * 
     * @param message 报文内容
     * @return 校验结果，包含格式是否正确和CRC校验结果
     */
    public static ValidationResult validateMessageFormatAndCrc(String message) {
        ValidationResult result = new ValidationResult();
        
        if (message == null || message.trim().isEmpty()) {
            result.setValid(false);
            result.setErrorMessage("报文为空");
            return result;
        }

        String protocolType = detectProtocolType(message);
        String clean = message;
        if (!"ASCII".equals(protocolType)) {
            clean = clean.replaceAll("\\s+", "").toUpperCase();
        }
        if (protocolType == null) {
            result.setValid(false);
            result.setErrorMessage("非SL651协议报文格式");
            return result;
        }
        
        // 验证报文基本格式
        boolean formatValid = false;
        String formatError = "";
        
        if ("ASCII".equals(protocolType)) {
            formatValid = validateAsciiFormat(clean, result);
        } else if ("HEX".equals(protocolType)) {
            formatValid = validateHexFormat(clean, result);
        } else {
            result.setValid(false);
            result.setErrorMessage("未知的SL651报文格式");
            return result;
        }
        
        if (!formatValid) {
            return result; // 如果格式校验失败，直接返回结果
        }
        
        // 验证CRC校验码
        boolean crcValid = false;
        if ("ASCII".equals(protocolType)) {
            crcValid = SL651CrcUtil.verifyAscillMessageCrc(clean);
        } else if ("HEX".equals(protocolType)) {
            crcValid = SL651CrcUtil.verifyHexMessageCrc(clean);
        } else {
            crcValid = SL651CrcUtil.verifyHexMessageCrc(clean);
        }

        result.setCrcValid(crcValid);
        if (!crcValid) {
            result.setErrorMessage("CRC校验失败");
        } else {
            result.setValid(true);
        }
        return result;
    }
    
    /**
     * 验证ASCII编码报文格式
     * 
     * ASCII编码报文结构：
     * - 起始符：1字节（2个字符, 01）
     * - 中心站地址：1字节HEX编码（2个字符）
     * - 遥测站地址：5字节HEX编码（10个字符）
     * - 密码：2字节HEX编码（4个字符）
     * - 功能码：1字节HEX编码（2个字符）
     * - 报文上行标识及长度：2字节HEX编码（4个字符）
     * - 数据起始符：1字节（2个字符, 02）
     * - 数据内容：可变长度（最少0个字符）
     * - 数据结束符：1字节（2个字符, 03）
     * - 校验码：2字节HEX编码（4个字符）
     * 
     * 理论最小长度：2 + 2 + 10 + 4 + 2 + 4 + 2 + 0 + 2 + 4 = 32个字符
     * 
     * @param message 清理后的ASCII编码报文
     * @param result 用于存储校验结果
     * @return 格式是否正确
     */
    private static boolean validateAsciiFormat(String message, ValidationResult result) {
        // 检查最小长度
        if (message.length() < 32) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足（最小32字符）");
            return false;
        }
        
        // 检查起始符
        if (!message.startsWith("01")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文起始符错误（应为01）");
            return false;
        }
        
        // 检查中心站地址（应在第3-4个字符位置）
        if (message.length() < 4) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少中心站地址");
            return false;
        }
        String centerAddr = message.substring(2, 4);
        if (!centerAddr.matches("[0-9A-F]{2}")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文中心站地址格式错误");
            return false;
        }
        
        // 检查遥测站地址（应在第5-14个字符位置）
        if (message.length() < 14) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少遥测站地址");
            return false;
        }
        String stationAddr = message.substring(4, 14);
        if (!stationAddr.matches("[0-9A-F]{10}")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文遥测站地址格式错误");
            return false;
        }
        
        // 检查密码（应在第15-18个字符位置）
        if (message.length() < 18) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少密码");
            return false;
        }
        String password = message.substring(14, 18);
        if (!password.matches("[0-9A-F]{4}")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文密码格式错误");
            return false;
        }
        
        // 检查功能码（应在第19-20个字符位置）
        if (message.length() < 20) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少功能码");
            return false;
        }
        String functionCode = message.substring(18, 20);
        // 功能码应为有效的十六进制字符
        if (!functionCode.matches("[0-9A-F]{2}")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文功能码格式错误");
            return false;
        }
        
        // 检查报文上行标识及长度（应在第21-24个字符位置）
        if (message.length() < 24) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少报文上行标识及长度");
            return false;
        }
        String flagAndLength = message.substring(20, 24);
        if (!flagAndLength.matches("[0-9A-F]{4}")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文上行标识及长度格式错误");
            return false;
        }
        
        // 检查数据起始符STX（应在第25-26个字符位置，值为02）
        if (message.length() < 26) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文长度不足，缺少数据起始符STX");
            return false;
        }
        if (!message.substring(24, 26).equals("02")) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文数据起始符STX错误（应为02）");
            return false;
        }
        
        // 检查数据结束符ETX（应存在且在CRC之前）
        int etxPos = message.lastIndexOf("03");
        if (etxPos == -1) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文缺少数据结束符ETX");
            return false;
        }
        
        if (message.length() - etxPos - 2 < 4) {
            result.setValid(false);
            result.setErrorMessage("ASCII编码报文数据结束符ETX错误或缺少CRC校验码");
            return false;
        }
        
        // 提取并存储基本信息
        result.setMessageType("ASCII");
        result.setCenterAddress(centerAddr);
        result.setStationAddress(stationAddr);
        result.setPassword(password);
        result.setFunctionCode(functionCode);
        result.setFunctionDescription(SL651CommonUtil.getFunctionCodeDescription(functionCode));
        
        // 保存数据长度和原始数据
        // 数据部分起始位置：SOH(2) + 中心站地址(2) + 遥测站地址(10) + 密码(4) + 功能码(2) + 报文上行标识及长度(4) + STX(2)
        int dataStartPos = 2 + 2 + 10 + 4 + 2 + 4 + 2; // = 26
        if (etxPos > dataStartPos) {
            result.setDataLength(etxPos - dataStartPos);
            result.setRawData(message.substring(dataStartPos, etxPos));
        } else {
            result.setDataLength(0);
            result.setRawData("");
        }
        
        return true;
    }
    
    /**
     * 验证HEX编码报文格式
     * 
     * HEX编码报文结构：
     * - 起始符：2字节 (4个字符, 7E7E)
     * - 中心站地址：1字节 (2个字符)
     * - 遥测站地址：5字节 (10个字符)
     * - 密码：2字节 (4个字符)
     * - 功能码：1字节 (2个字符)
     * - 报文上行标识及长度：2字节 (4个字符)
     * - 数据起始符：1字节 (2个字符, 02)
     * - 数据内容：可变长度（最少0个字符）
     * - 数据结束符：1字节 (2个字符, 03)
     * - CRC校验码：2字节 (4个字符)
     * 
     * 最小长度：4 + 2 + 10 + 4 + 2 + 4 + 2 + 0 + 2 + 4 = 34个字符
     * 
     * @param message 清理后的HEX编码报文
     * @param result 用于存储校验结果
     * @return 格式是否正确
     */
    private static boolean validateHexFormat(String message, ValidationResult result) {
        // 检查最小长度
        if (message.length() < 34) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足（最小34字符）");
            return false;
        }
        
        // 检查起始符
        if (!message.startsWith("7E7E")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文起始符错误（应为7E7E）");
            return false;
        }
        
        // 检查中心站地址（应在第5-6个字符位置）
        if (message.length() < 6) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少中心站地址");
            return false;
        }
        String centerAddr = message.substring(4, 6);
        if (!centerAddr.matches("[0-9A-F]{2}")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文中心站地址格式错误");
            return false;
        }
        
        // 检查遥测站地址（应在第7-16个字符位置）
        if (message.length() < 16) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少遥测站地址");
            return false;
        }
        String stationAddr = message.substring(6, 16);
        if (!stationAddr.matches("[0-9A-F]{10}")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文遥测站地址格式错误");
            return false;
        }
        
        // 检查密码（应在第17-20个字符位置）
        if (message.length() < 20) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少密码");
            return false;
        }
        String password = message.substring(16, 20);
        if (!password.matches("[0-9A-F]{4}")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文密码格式错误");
            return false;
        }
        
        // 检查功能码（应在第21-22个字符位置）
        if (message.length() < 22) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少功能码");
            return false;
        }
        String functionCode = message.substring(20, 22);
        // 功能码应为有效的十六进制字符
        if (!functionCode.matches("[0-9A-F]{2}")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文功能码格式错误");
            return false;
        }
        
        // 检查报文上行标识及长度（应在第23-26个字符位置）
        if (message.length() < 26) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少报文上行标识及长度");
            return false;
        }
        String flagAndLength = message.substring(22, 26);
        if (!flagAndLength.matches("[0-9A-F]{4}")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文上行标识及长度格式错误");
            return false;
        }
        
        // 检查数据起始符STX（应在第27-28个字符位置，值为02）
        if (message.length() < 28) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文长度不足，缺少数据起始符STX");
            return false;
        }
        if (!message.substring(26, 28).equals("02")) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文数据起始符STX错误（应为02）");
            return false;
        }
        
        // 检查数据结束符ETX（应存在且在CRC之前）
        int etxPos = message.lastIndexOf("03");
        if (etxPos == -1) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文缺少数据结束符ETX");
            return false;
        }
        
        // 检查ETX后是否有足够的CRC校验码（至少4字符）
        if (message.length() - etxPos - 2 < 4) {
            result.setValid(false);
            result.setErrorMessage("HEX编码报文数据结束符ETX错误或缺少CRC校验码");
            return false;
        }
        
        // 提取并存储基本信息
        result.setMessageType("HEX");
        result.setCenterAddress(centerAddr);
        result.setStationAddress(stationAddr);
        result.setPassword(password);
        result.setFunctionCode(functionCode);
        result.setFunctionDescription(SL651CommonUtil.getFunctionCodeDescription(functionCode));
        
        // 保存数据长度和原始数据
        if (etxPos > 28) {
            result.setDataLength(etxPos - 28);
            result.setRawData(message.substring(28, etxPos));
        } else {
            result.setDataLength(0);
            result.setRawData("");
        }
        
        return true;
    }

    /**
     * 报文校验结果类
     */
    @Data
    public static class ValidationResult {
        private boolean isValid = false;
        private boolean crcValid = false;
        private String errorMessage = "";
        private String messageType = "";
        private String centerAddress = "";
        private String stationAddress = "";
        private String password = "";
        private String functionCode = "";
        private String functionDescription = "";
        
        // 添加更多字段用于存储报文详细信息
        private String observeTime = "";
        private int dataLength = 0;
        private String rawData = "";

        private String getValidateInfo() {
            return "--------------报文格式校验信息--------------"  + "\n" +
                    "报文校验结果：" + (isValid? "通过" : "失败") + "\n" +
                   "错误信息：" + errorMessage + "\n" +
                    "报文类型：" + messageType + "\n" +
                   "中心站地址：" + centerAddress + "\n" +
                   "遥测站地址：" + stationAddress + "\n" +
                   "密码：" + password + "\n" +
                   "功能码：" + functionCode + "（" + functionDescription  + "）" + "\n" +
                    "CRC校验：：" + (crcValid? "通过" : "失败")  + "\n";
        }
    }
}